Skip to content

Introducing a file-based database wlx. - #40

Open
pplupo wants to merge 228 commits into
doublecmd:masterfrom
pplupo:dbview
Open

Introducing a file-based database wlx.#40
pplupo wants to merge 228 commits into
doublecmd:masterfrom
pplupo:dbview

Conversation

@pplupo

@pplupo pplupo commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

It can open and export data from the most popular file-based databases. Some have SQL execution capabilities, and some can even change data. Check the readme file for detailed info.

pplupo and others added 30 commits May 12, 2026 10:08
This commit introduces the mpv_wayland WLX (Lister) plugin for Double
Commander. It enables native video playback in the Quick View panel
using the libmpv Render API and QOpenGLWidget. It is specifically
designed for seamless integration on Wayland and HiDPI displays.

Committed files:
- Source code (src/) implementing Qt6/mpv integration
- SDK headers (sdk/) for plugin interface
- CMakeLists.txt for build configuration
- README.md with installation instructions and embedded screenshot
- mpv_wayland.png screenshot asset
- Integration into root build.sh for automated release packaging
This commit introduces the MDK WLX plugin, a high-performance media
viewer powered by the MDK SDK and pure Qt6. It allows instant preview
of video and audio files. A key feature is its out-of-process isolation
via dlopen, ensuring that MDK's dependencies do not conflict with
Double Commander.

Committed files:
- C++ source (plugin.cpp) and Pascal wrappers (mdk.pas, etc.)
- Lazarus project files (.lpi, .lpr)
- Makefile for building the C++ shared object
- SDK headers (sdk/)
- README.md and proposal.md documentation
- Integration into root build.sh for automated release packaging
This commit introduces a specialized log viewer WLX plugin built with
Qt6 and C++20. It is designed to handle massive log files without
freezing Double Commander, utilizing zero-copy file loading (mmap),
fast regex searching (RE2), and a robust focus-isolation architecture
for Wayland.

Committed files:
- Source code (src/) for the log model and viewer widget
- CMakeLists.txt for build configuration
- README.md and detailed design doc (double commander wlx log viewer.md)
- logviewer.png screenshot asset
- Integration into root build.sh for automated release packaging
This commit introduces the kpart WLX plugin. It acts as a host for
KDE KParts, effectively allowing Double Commander to leverage any
installed KDE viewer (like Okular for PDFs or LibreOffice for docs)
directly within the Quick View panel natively on Wayland/Qt6.

Committed files:
- Source code (src/) for KPart hosting logic
- SDK headers (sdk/)
- CMakeLists.txt for build configuration
- README.md with screenshots
- test.svg and screenshot assets (kpart_md.png, kpart_svg.png)
- Integration into root build.sh for automated release packaging
…ete row editing, TSV support, and context menu

- Display CSV/TSV files in an interactive QTableWidget grid
- Auto-detect separator (comma, semicolon, tab); fall back to file extension
- Header Row toggle button: interprets first line as header or data row,
  reloads file on toggle; controls whether copies include the header line
- Ctrl+C: copy selection as TSV (includes header when Header Row is on)
- Right-click context menu: Copy as TSV, Copy as CSV, Delete Selected Rows,
  Insert from Clipboard
- Ctrl+V: insert clipboard rows at selected position; validates column count;
  skips clipboard header line when it matches the current header (Header Row on)
- Delete: remove selected rows
- Ctrl+S: save file; correctly handles editing vs non-editing state without
  disturbing Double Commander focus
- Inline cell editing with Save / Save As / Reload toolbar actions
- Enca encoding auto-detection with Glib conversion fallback
- Double-quoted field parsing per CSV RFC
- F7 search support
- Full README with screenshots, feature docs, and configuration reference
- Attribution to original author j2969719
…nd highlighting

- Export ListSetDefaultParams to retrieve and bind the host's INI file path.
- Add HighlightRule structure and fast RE2-based line matching directly on memory-mapped offsets.
- Create Highlighting Rules SettingsDialog modal with an inline-colored rules list.
- Enable ExtendedSelection on the rules list to support deleting or moving multiple rules up/down, maintaining selection state using QItemSelectionModel.
- Connect double-click on any rule row to open the editing window.
- Write Foreground/Background labels above color picker buttons in RuleDialog.
- Implement session persistence under [HighlightRules] in the INI file.
- Prevent default rules from overriding empty rule configurations on restart.
- Add context-aware "Add Default Rules" button to prepend default log levels relative to the current selection.
- Create sample.log matching each level for diagnostics.
This commit introduces a premium editor WLX plugin based on Native Qt6
and KDE's KTextEditor framework. It brings full syntax highlighting,
code folding, and advanced editing capabilities to Double Commander's
viewer panel without LCL compatibility crashes on Wayland.

Committed files:
- Source code (src/) wrapping KTextEditor
- CMakeLists.txt for build configuration
- README.md with screenshots
- defects.md tracking known issues and resolutions
- Screenshot assets (kate_java.png, kate_md.png, kate_py.png)
- Integration into root build.sh for automated release packaging
…yles, preserve selection, and fix Proper/Title Case logic
…nd highlighting

- Export ListSetDefaultParams to retrieve and bind the host's INI file path.
- Add HighlightRule structure and fast RE2-based line matching directly on memory-mapped offsets.
- Create Highlighting Rules SettingsDialog modal with an inline-colored rules list.
- Enable ExtendedSelection on the rules list to support deleting or moving multiple rules up/down, maintaining selection state using QItemSelectionModel.
- Connect double-click on any rule row to open the editing window.
- Write Foreground/Background labels above color picker buttons in RuleDialog.
- Implement session persistence under [HighlightRules] in the INI file.
- Prevent default rules from overriding empty rule configurations on restart.
- Add context-aware "Add Default Rules" button to prepend default log levels relative to the current selection.
- Create sample.log matching each level for diagnostics.
Manually merged unique changes from all editor-related branches:

- editor-features: focus management (NoFocus on menuBar, focus restoration,
  focus-stealing prevention), Save/Save As/Save Copy As actions with toolbar
  button, Print action with toolbar button, font zoom controls (Ctrl++/Ctrl+-/
  Ctrl+0) with native KTE action fallback, Force RTL Direction toggle, Ctrl+Shift+S
  Save As shortcut precedence fix, QTimer-based focus restoration

- kate-case-conversion: Capitalization submenu replacing flat menu items,
  improved Title Case with minor-word handling, fixed Proper Case (toLower
  instead of preserve), renamed snail_case to snake_case, added PascalCase,
  SCREAMING_SNAKE_CASE, kebab-case, Sentence case, SCREAMING-KEBAB-CASE,
  dot.case, path/case, replaceSelectionPreservingRange helper to restore
  selection after text replacement

- fix-redo-and-write-lock: Ctrl+Shift+Z redo shortcut (in addition to Ctrl+Y),
  extracted toggleReadOnly() helper, Alt+Shift+R shortcut for read-only toggle,
  fixed double-trigger bug by switching QAction::toggled to QAction::triggered,
  added shortcut labels to read-only menu action
…agation

- Remove non-standard `lc_focus` definition from `sdk/wlxplugin.h` and its handler in `wlx_plugin.cpp`.

- Remove custom `hostSetFocus` method from `EditorWidget`.

- Set the editor view as the focus proxy for the main widget.

- Use native Qt event filtering and focus change listeners to handle active state transitions.
This makes the plugin standalone by compiling and linking libenca statically, removing the need for users to manually install libenca.so.0.
Adds ability to insert empty rows or clipboard rows above/below current selection or clicked row.
pplupo and others added 30 commits July 13, 2026 10:31
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Focus/input:
- Add lc_focus (5) to the shared wlxplugin.h SDK header, previously missing
  entirely on this branch, so ListSendCommand could handle DC's focus
  hand-off signal at all.
- Port QtWlPlugin::FocusManager (from wayland_qt_base) into officeview for
  both the x2t/PdfViewerWidget and LibreOfficeKit/LOKContainerWidget paths,
  replacing ad-hoc focus handling that left keyboard shortcuts and panel
  switching unreliable.
- Fix a click-activation bug where the ancestry check used the wrong
  QObject in the propagation chain (QWidgetWindow isn't a QWidget), causing
  some documents to require two clicks before Ctrl+C worked.
- Add a grace-period guard (setActiveFromHost) so DC's own lc_focus(0)
  can't immediately undo a legitimate click-based activation.
- Retry LOK's getTextSelection after SelectAll instead of a single fixed
  wait, fixing intermittent empty-clipboard copies.
- Scope LOK copy/context-menu actions to the specific sheet/slide clicked
  or currently visible, instead of whatever part LOK's internal cursor
  last happened to be on.

Rendering:
- Give ODF documents their own LibreOfficeKit-side zoom, copy, and context
  menu instead of silently borrowing x2t's, since ODF stays on LibreOffice
  deliberately (better fidelity than x2t for this format family).
- Fix spreadsheet sheet-tab extraction to exclude hidden sheets, which
  otherwise mismatched x2t's actual visible-sheet PDF output and silently
  hid the tab bar.
- Add per-sheet pagination for xlsx/xlsm: convert each sheet separately
  (patching its own temp copy's activeTab so x2t paginates it fully,
  instead of squeezing every sheet onto a single page via printPages:all)
  and merge with qpdf, tracking real per-sheet start pages instead of
  assuming one page per sheet.
- Switch the PDF view to always use MultiPage mode; SinglePage mode hid a
  sheet's later pages behind the tab bar with no obvious way to reach them.
- Apply 2x supersampled LOK tile rendering to reduce font aliasing.

Config:
- Add a per-extension file size limit (officeview.conf), skipping
  conversion entirely for files over the limit; 0 disables an extension.
- Restructure officeview.conf into [Paths]/[Engines]/[FileSizeLimits]
  sections (previously a flat list that only grew entries lazily, so it
  never had all supported extensions) with paths first and a comment
  explaining the size-limit semantics.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
QPdfView's page-layout/coordinate-mapping internals are private API with
no public hook to map a click to a page + point-within-page, which meant
copy was limited to whole-page/whole-selection text with no way to
highlight or select an arbitrary run of text. MuPDF's structured-text API
(fz_highlight_selection / fz_copy_selection) exposes that directly.

- Add MuPdfWidget/MuPdfContainerWidget, mirroring LOKWidget/
  LOKContainerWidget's structure (continuous page stacking, only visible
  pages rendered, same FocusManager/zoom/wheel-event wiring) so all three
  rendering paths share one interface for ListSendCommand.
- Remove PdfViewerWidget and the Qt6::Pdf/PdfWidgets dependency entirely;
  replace the one QPdfDocument use (page counting for the per-sheet merge
  pipeline) with a small MuPDF-based helper.
- Fix tab-bar desync: sheetStartPages holds page *indices*, but three
  places compared it directly against the scrollbar's pixel position, so
  a few pixels of scroll could exceed a page-index number like 14 or 15
  and snap the active tab to the last sheet. Convert through
  pageYOffset()/pageAtY() so both sides of every comparison share units.
- Fix font aliasing on fractional-scaling displays: page rendering had no
  devicePixelRatio handling, so Qt stretched the under-resolution image up
  to fill the widget's real physical pixels. Render at
  zoom*devicePixelRatioF() and mark the QImage's devicePixelRatio so Qt
  draws it 1:1 instead of scaling it.

Links dynamically against the system libmupdf for now; static linking is
a separate follow-up.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Also update the compiled binary to match the current MuPDF-based build.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
rclone's Google Drive VFS mount leaves native Google Docs/Sheets/Slides
as 0-byte stub files on disk, since it can't materialize their content
transparently on read like a regular file. When ListLoad hits a 0-byte
file for a supported extension, it now checks /proc/mounts for a
covering fuse.rclone mount, and if found runs `rclone copyto` to export
the real content before handing it to the usual OOXML/ODF rendering
path. If the file isn't under an rclone mount, it fails gracefully
with a short message instead of trying to render nothing.

Google Drive exports get their own independently configurable engine
preference (EngineForGDrive), and any of the four Engines keys can now
be set to Disabled to skip that format family entirely.

The file-size-limit disable sentinel changes from 0 to -1, since 0 is
now a legitimate size for an unmaterialized rclone stub rather than
something to reject outright.

README updated to document all of the above.

Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…rebird

- Link duckdb_static instead of the shared duckdb target: the shared
  target produced a dbview_qt6.wlx that referenced libduckdb.so at a raw
  build-tree path (confirmed via ldd), which would not resolve on another
  machine at all.

- Vendor LMDB 1.0.1 (OpenLDAP Public License) directly under
  src/liblmdb/, built as a static lib, replacing a plain system
  liblmdb.so/.a dependency.

- Vendor and statically link Berkeley DB 5.3.28 via ExternalProject_Add
  (ships its own autotools build, patched for GCC 14+ compatibility and
  -fPIC). Deliberately NOT the newer system "db" package: that version is
  AGPL-3.0-only (Oracle's post-6.0 relicensing), which would place this
  whole plugin binary under AGPL's copyleft/network-use terms. 5.3.28 is
  the last release under the original permissive Sleepycat license,
  confirmed from its own LICENSE file, and is what Debian/Ubuntu also
  deliberately stayed on for the same reason.

- Vendor the SQLite amalgamation (public domain) under src/libsqlite3/
  and rewrite SqliteEngine off QSqlDatabase/QSqlTableModel onto the raw
  sqlite3 C API directly, via a new SqliteTableModel (QAbstractTableModel
  using rowid-based parameterized UPDATEs, same open-transaction/
  commit/rollback pattern as DuckDbEngine).

- Rewrite FirebirdEngine off QSqlDatabase/QIBASE onto the classic
  isc_dsql_*/XSQLDA C API directly, via a new FirebirdTableModel
  (RDB$DB_KEY standing in for SQLite's rowid). Gated behind
  find_library(fbclient)/find_path(ibase.h) so the build degrades
  gracefully when libfbclient isn't installed, rather than failing the
  whole plugin.

- Drop Qt6::Sql from the plugin entirely — nothing needs it anymore.
  Removed the now-dead QSqlTableModel/QSqlRecord/QSqlField dispatch
  branches in DbViewWidget.cpp in favor of SqliteTableModel/
  FirebirdTableModel ones.

- Fix a pre-existing unrelated build break: DbViewWidget.cpp called a
  ThemeManager::detectSystemTheme() that doesn't exist anywhere in
  ThemeManager; swapped to currentTheme() to unblock building at all,
  flagged inline as a FIXME pending a decision on intended behavior.

Verified: full dbview_qt6.wlx builds and links clean (confirmed via ldd
that duckdb/lmdb/db/sqlite3 no longer appear as dynamic dependencies,
only libfbclient remains, as intended). SQLite and Firebird engines both
exercised end-to-end with a standalone test harness against real
databases (SQLite via the sqlite3 CLI, Firebird via the official
prebuilt 5.0.4 release's embedded engine) — open, list tables, read
cells including BLOB display, edit, commit, close, reopen, and confirm
the edit persisted. All checks passed for both engines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The SQL/KV engines were already de-Qtified in an earlier session phase
(vendored libsqlite3, statically-linked lmdb/Berkeley DB 5.3.28, duckdb_static)
but the engine classes themselves stayed QObject/QAbstractTableModel-derived
(QString/QVariant/QAbstractItemModel* APIs) -- fine for the Qt UI, not
reusable from GTK. This adds a parallel, genuinely toolkit-neutral
DbEngineCore hierarchy on top of the same underlying C/C++ database APIs:

- DbEngineCore: abstract interface (open/close, tableNames/viewNames/
  columnInfos, selectTable/selectQuery, cellText/cellIsBinary/setCellText,
  submitAll/revertAll). Loads a table's full result set into memory on
  select (a deliberate simplification vs. the Qt side's windowed/lazy
  QAbstractTableModel classes -- most of them effectively did this anyway).
- SqliteEngineCore / DuckDbEngineCore: same rowid-based UPDATE pattern as
  the Qt side, using the same vendored sqlite3 / duckdb_static.
- KeyValueEngineCoreBase + LmdbEngineCore / BdbEngineCore (+ RocksDbEngineCore
  / LevelDbEngineCore behind the existing ENABLE_ROCKSDB_LEVELDB flag): a
  "keys" pseudo-table, full eager load + immediate put-on-edit (vs. the Qt
  side's windowed cache + buffered submit/revert).
- MdbEngineCore: MS Access, read-only (matches the Qt side).
- FirebirdEngineCore: read-only browsing + custom SELECTs via the same
  classic isc_dsql_*/XSQLDA API as FirebirdTableModel -- cell editing via
  RDB$DB_KEY was deliberately not duplicated here (the Qt implementation
  itself is flagged unverified/no live Firebird available when it was
  written; not worth doubling that risk in a second implementation).
- DbEngineCoreFactory: same detection order/fallback chain as DbEngine::
  createForFile.

GTK3 UI (src/gtk3/plugin_gtk3.cpp): GtkTreeStore schema tree (Tables/Views)
plus wlxbase_gtk's GtkEditableGridWidget (rebuilt per table selection, same
reason as officeview/structview), Submit/Revert toolbar, a query console
for SQL engines, ListSearchText scanning the grid.

Also pulls wlxbase_gtk onto this branch (same dependency as officeview/
structview).

Verified with a real dlopen+ListLoad ABI test against 4 freshly-created,
real database files (via each engine's own C API, independent of our
GTK core): a SQLite DB with a 2-row table, a DuckDB file, an LMDB
environment, and a Berkeley DB hash file. All 4 open, find the actual
inserted values via ListSearchText, copy, and close cleanly with no
crash. Firebird/RocksDB/LevelDB gracefully degrade out of the build on
this machine (no libfbclient here, ENABLE_ROCKSDB_LEVELDB defaults off),
same as the Qt6 build already did. nm -D confirms all 5 WLX entry points
exported; ldd confirms zero Qt linkage.

Qt6 build unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eal DB_KEY editing

Two follow-up fixes to the GTK core added in the previous commit:

1. Chunked fetching (was: full table loaded into memory on select)

   DbEngineCore's selectTable()/selectQuery() now only prepare the query
   and load the first chunk (200 rows); rowCount() reports the true total
   (via COUNT(*) or equivalent) while fetchedRowCount() reports what's
   actually materialized. fetchMore()/canFetchMore() pull additional
   chunks on demand -- same principle as the Qt side's
   QAbstractItemModel::fetchMore()/canFetchMore() (which DuckDbModel
   already used; KeyValueModel approximated it with a recentering random-
   access window, simplified here to a sequential forward cursor since the
   GTK grid only ever scrolls forward through what's been appended).

   Implemented for every engine: SqliteEngineCore/DuckDbEngineCore
   (LIMIT/OFFSET chunking), KeyValueEngineCoreBase (shared cursor-based
   fetchWindow, used by Lmdb/Bdb/RocksDb/LevelDb), MdbEngineCore (keeps
   libmdb's forward-only row cursor open across fetchMore() calls instead
   of re-rewinding+skipping every time), FirebirdEngineCore (keeps the
   isc_dsql_fetch cursor open across calls).

   wlxbase_gtk's GtkEditableGridWidget gained appendRows() (adds to the
   existing GtkListStore instead of replacing it). dbview's GTK UI loads
   only the first chunk into the grid, then hooks the scrolled window's
   vertical GtkAdjustment: nearing the bottom of what's loaded triggers
   fetchMore() and appends just the new rows.

   Verified two ways: a standalone core-only test against a real 600-row
   SQLite table and a real 500-row DuckDB table shows fetchedRowCount()
   staying at 200 immediately after selectTable() (rowCount() reporting
   the true total), growing via fetchMore() calls to the full count. And
   through the actual GTK ABI: ListSearchText finds a value planted in row
   150 (within the first chunk) but NOT one planted in row 599 (beyond
   it) -- proving the grid genuinely isn't populated beyond what's been
   scrolled to.

2. Firebird: vendored embedded engine, verified RDB$DB_KEY editing (both Qt and GTK)

   This plugin only ever deals with file-based, in-process databases
   (same as SQLite/DuckDB/LMDB/Berkeley DB) -- it was never meant to talk
   to a Firebird *server*. The previous CMake used a bare
   find_library(fbclient) against whatever the system provides, which
   doesn't guarantee the paired firebird.conf/plugins actually behave as
   a local, in-process engine (a distro's fbclient could be configured
   server-oriented, or missing the engine plugin's client library
   entirely). Fixed by vendoring a minimal, verified-working embedded
   Firebird 5.0.4 runtime under thirdparty/firebird-embedded/
   (lib/libfbclient.so + its libtomcrypt dependency, plugins/{libEngine13,
   libChaCha,libLegacy_Auth,libLegacy_UserManager,libSrp}.so,
   firebird.conf, firebird.msg, include/) -- same vendoring policy already
   used for sqlite3/lmdb/duckdb/Berkeley DB. Firebird's client library
   self-locates its conf/msg/plugins relative to its own resolved .so path
   via dladdr (confirmed: works with zero FIREBIRD/ISC_* env vars set,
   purely from this directory's lib/plugins/conf/msg layout), so both
   dbview_qt6 and dbview_gtk3 now get a $ORIGIN-relative RPATH into
   firebird-embedded/lib/, and the directory is copied next to the built
   .wlx post-build. Since a bare filesystem path (no "host:" prefix) is
   the only kind of connection string this plugin ever constructs,
   Firebird's client library loads the storage engine in-process and never
   opens a network connection -- no server process is ever involved, in
   either the Qt or GTK build.

   Also ported RDB$DB_KEY-based cell editing to FirebirdEngineCore (GTK),
   matching FirebirdTableModel's approach on the Qt side (which turned out
   to already work correctly -- the "unverified" flag in its own comments
   predates this session's actual testing against a real database, done
   here with the now-vendored engine).

   Verified with the real embedded engine, no manually-set FIREBIRD/
   LD_LIBRARY_PATH (env -i clean environment): a standalone core-only test
   opens a real .fdb, browses EMPLOYEES, edits a SALARY cell via
   setCellText(), submits, closes, reopens, and confirms the edit
   persisted -- full round trip, both through DbEngineCore directly and
   through the actual dbview_gtk3.wlx ABI (dlopen + ListLoad).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants